# Always print this out before your assignment
sessionInfo()
getwd()
library('tidyverse')
-- Attaching packages ---------------------------------------------------------------------------------------------------------------------- tidyverse 1.3.1 --
v ggplot2 3.3.5 v purrr 0.3.4
v tibble 3.1.5 v stringr 1.4.0
v tidyr 1.1.4 v forcats 0.5.1
v readr 2.0.2
-- Conflicts ------------------------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
x readr::col_factor() masks scales::col_factor()
x purrr::discard() masks scales::discard()
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
library("fs")
library('here')
here() starts at C:/Users/cabrooke/Documents/R/696/group project/final project/final_project
library('dplyr')
library('tidyverse')
library('ggplot2')
library('ggrepel')
library('ggthemes')
library('forcats')
library('rsample')
library('lubridate')
Attaching package: ‘lubridate’
The following objects are masked from ‘package:base’:
date, intersect, setdiff, union
library('ggthemes')
library('kableExtra')
Attaching package: ‘kableExtra’
The following object is masked from ‘package:dplyr’:
group_rows
library('pastecs')
Attaching package: ‘pastecs’
The following object is masked from ‘package:tidyr’:
extract
The following objects are masked from ‘package:dplyr’:
first, last
library('viridis')
Loading required package: viridisLite
Attaching package: ‘viridis’
The following object is masked from ‘package:scales’:
viridis_pal
library('plotly')
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
library('tidyquant')
Loading required package: PerformanceAnalytics
Loading required package: xts
Loading required package: zoo
Attaching package: ‘zoo’
The following objects are masked from ‘package:base’:
as.Date, as.Date.numeric
Attaching package: ‘xts’
The following objects are masked from ‘package:pastecs’:
first, last
The following objects are masked from ‘package:dplyr’:
first, last
Attaching package: ‘PerformanceAnalytics’
The following object is masked from ‘package:graphics’:
legend
Loading required package: quantmod
Loading required package: TTR
Registered S3 method overwritten by 'quantmod':
method from
as.zoo.data.frame zoo
== Need to Learn tidyquant? ===================================================================================================================================
Business Science offers a 1-hour course - Learning Lab #9: Performance Analysis & Portfolio Optimization with tidyquant!
</> Learn more at: https://university.business-science.io/p/learning-labs-pro </>
library('scales')
Final Project Cleaning and Summary Statistics
1a) Loading data
#Reading the data in and doing minor initial cleaning in the function call
#Reproducible data analysis should avoid all automatic string to factor conversions.
#strip.white removes white space
#na.strings is a substitution so all that have "" will = na
data <- read.csv(here::here("final_project", "donor_data.csv"),
stringsAsFactors = FALSE,
strip.white = TRUE,
na.strings = "")
1b) Fixing the wonky DOB & Data cleanup
glimpse(data_cleaned$zipslry_range)
logi [1:323000] NA NA NA NA NA NA ...
1c Creating factor variable for sex and married
data_cleaned <-
data_cleaned %>%
mutate(sex_fct =
fct_explicit_na(Sex)
)
data_cleaned <-
data_cleaned %>%
mutate(
sex_simple =
fct_lump_n(Sex, n = 4)
)
#checking to see if its a factor
class(data_cleaned$sex_fct)
#checking levels
levels(data_cleaned$sex_simple)
#creating a table against Sex column
table(data_cleaned$sex_fct, data_cleaned$sex_simple)
#making married a factor
data_cleaned_columns <-
data_cleaned_columns %>%
mutate(married_fct =
fct_explicit_na(Married)
)
#checking to see if its a factor
class(data_cleaned$married_fct)
1d #Mean, Median, and Count of Giving in Age Ranges
age_range_giving <- datacleaning %>%
group_by(age_range) %>%
summarise(avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
med_giving = median(HH.Lifetime.Giving, na.rm = TRUE),
amount_of_people_in_age_range = n())
Question 2
DonorSegment Analysis
#grouping by donorsegment and analyzing
data_cleaned_columns %>%
group_by(Donor.Segment) %>%
summarise(Count = length(Donor.Segment),
mean_total_giv = mean(HH.Lifetime.Giving)) %>%
arrange(-Count) %>%
filter(Count >= 100) %>%
#added scales package to have the values show in dollar
mutate(mean_total_giv = dollar(mean_total_giv)) %>%
kable(col.names = c("Donor Segment", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
kable_styling(bootstrap_options = c("striped", "hover"),
full_width = F)
| Donor Segment |
Count |
Mean HH Lifetime Giving |
| NA |
232033 |
$0 |
| Lost Donor |
69733 |
$4,958 |
| Lapsed Donor |
11220 |
$11,195 |
| Current Donor |
5704 |
$104,142 |
| Lapsing Donor |
3879 |
$16,595 |
| At-Risk Donor |
657 |
$85,198 |
NA
NA
2a) Plotting average giving by age range
ggplot(age_range_giving, aes(avg_giving, age_range)) +
geom_bar(stat = "identity")

NA
NA
2b) Count of donors based on age range (another way to look at it)
ggplot(datacleaning,
aes(age_range)) +
geom_bar() +
theme(axis.text.x = element_text(angle=45,
hjust=1)) +
labs(title = "Count of Age Ranges", x = "", y = "")

NA
NA
2c) Boxplot of the Age Ranges Against the Lifetime Giving Amounts with a log scale applied - the reason we applied log scale is to resolve issues with visualizations that skew towards large values in our dataset.
ggplot(datacleaning, aes(age_range,HH.Lifetime.Giving,fill = age_range)) +
geom_boxplot(
outlier.colour = "red") +
scale_y_log10() +
theme(axis.text.x=element_text(angle=45,hjust=1))
Warning: Transformation introduced infinite values in continuous y-axis
Warning: Removed 232033 rows containing non-finite values (stat_boxplot).

NA
NA
2d) Splitting by age and gender
#creating boxplots
datacleaning %>%
filter(Age < 100) %>% #removing the weird outliers that are over 100
filter(Sex %in% c("M", "F")) %>%
ggplot(aes(Sex, Age)) +
geom_boxplot() +
theme_economist() +
ggtitle("Ages of Donors Based on Gender") +
xlab(NULL) + ylab(NULL)

NA
NA
NA
NA
NA
2e) Distribution of people in the states that they live.
datacleaning %>%
mutate(State = ifelse(State == " ", "NA", State)) %>%
filter(State != "NA") %>%
group_by(State) %>%
summarise(Count = length(State)) %>%
filter(Count > 800) %>%
arrange(-Count) %>%
kable(col.names = c("Donor's State", "Count")) %>%
kable_styling(bootstrap_options = c("condensed"),
full_width = F)
| Donor's State |
Count |
| CA |
176695 |
| WA |
7958 |
| TX |
7268 |
| NY |
5661 |
| CO |
5073 |
| AZ |
4929 |
| OR |
4613 |
| FL |
4111 |
| IL |
3681 |
| HI |
3394 |
| PA |
2904 |
| OH |
2754 |
| NV |
2715 |
| MI |
2524 |
| MA |
2473 |
| NJ |
2311 |
| VA |
2158 |
| NC |
2087 |
| GA |
2045 |
| MO |
1889 |
| MN |
1732 |
| MD |
1488 |
| TN |
1443 |
| IN |
1417 |
| CT |
1380 |
| WI |
1330 |
| UT |
1174 |
| OK |
1151 |
| AL |
1120 |
| LA |
1110 |
| ID |
1096 |
| SC |
1076 |
| KY |
1032 |
| KS |
1027 |
| NM |
982 |
| IA |
880 |
NA
NA
NA
NA
NA
NA
2f) Looking at all donors first gift amount. 75% made a first gift of <100.
no_non_donors <- datacleaning %>%
filter(Lifetime.Giving != 0)
nd <- quantile(no_non_donors$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)
nd <- as.data.frame(nd)
nd %>%
kable(col.names = "Quantile") %>%
kable_styling(bootstrap_options = c("striped", "hover"),
full_width = F)
| |
Quantile |
| 25% |
3.8 |
| 50% |
25.0 |
| 75% |
100.0 |
| 90% |
500.0 |
| 99% |
15000.0 |
NA
NA
NA
NA
Modeling for you
3a) Linear model
#converting married Y and N to 1 and 0
datacleaning <- datacleaning %>%
mutate(Married_simple = ifelse(Married == "N",0,1))
mod1lm <- lm( Married_simple ~ Lifetime.Giving,
data = datacleaning)
summary(mod1lm)
Call:
lm(formula = Married_simple ~ Lifetime.Giving, data = datacleaning)
Residuals:
Min 1Q Median 3Q Max
-0.4107 -0.2872 -0.2872 0.7128 0.7128
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.287196453375 0.000796143577 360.73 <0.0000000000000002 ***
Lifetime.Giving 0.000000006818 0.000000007174 0.95 0.342
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.4525 on 323224 degrees of freedom
Multiple R-squared: 2.795e-06, Adjusted R-squared: -2.991e-07
F-statistic: 0.9033 on 1 and 323224 DF, p-value: 0.3419
Kmeans
is.numeric(data_cleaned$HH.Lifetime.Giving)
[1] TRUE
3a)
p <- datacleaning %>%
ggplot(aes(Age)) + geom_histogram(bins=30, fill = "blue") + theme_economist_white() +
ggtitle("Overall Donor Age Distribution") +
xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(5,100,by = 20)) +
scale_y_continuous(breaks = seq(20,100,by = 20)) + xlim(c(20,100))
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
ggplotly(p)
Warning: Removed 199288 rows containing non-finite values (stat_bin).
p
Warning: Removed 199288 rows containing non-finite values (stat_bin).
Warning: Removed 2 rows containing missing values (geom_bar).

ggplot(data = datacleaning, aes(x = Age)) + geom_histogram(fill ="blue")+ xlim(c(20,100))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 199288 rows containing non-finite values (stat_bin).
Warning: Removed 2 rows containing missing values (geom_bar).

NA
NA
NA
LS0tDQp0aXRsZTogIkJST0NPREUgU3VtbWFyeSBTdGF0aXN0aWNzIg0KYXV0aG9yOiAiQWFyb24sIENhbm5vbiwgSm9zaCwgUnlhbiINCnN1YnRpdGxlOiBGaW5hbCBQcm9qZWN0IFN1bW1hcnkgU3RhdGlzdGljcw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQoNCiMgUGxlYXNlIGxlYXZlIHRoaXMgY29kZSBjaHVuayBhcyBpcy4gSXQgbWFrZXMgc29tZSBzbGlnaHQgZm9ybWF0dGluZyBjaGFuZ2VzIHRvIGFsdGVyIHRoZSBvdXRwdXQgdG8gYmUgbW9yZSBhZXN0aGV0aWNhbGx5IHBsZWFzaW5nLiANCg0KbGlicmFyeShrbml0cikNCg0KDQojIENoYW5nZSB0aGUgbnVtYmVyIGluIHNldCBzZWVkIHRvIHlvdXIgb3duIGZhdm9yaXRlIG51bWJlcg0Kc2V0LnNlZWQoMTgxOCkNCm9wdGlvbnMod2lkdGg9NzApDQpvcHRpb25zKHNjaXBlbj05OSkNCg0KDQojIHRoaXMgc2V0cyB0ZXh0IG91dHB1dHRlZCBpbiBjb2RlIGNodW5rcyB0byBzbWFsbA0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGgud3JhcD01MCksdGlkeT1UUlVFLCBzaXplID0gInZzbWFsbCIpICANCm9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICMgImNhY2hpbmciIHN0b3JlcyBvYmplY3RzIGluIGNvZGUgY2h1bmtzIGFuZCBvbmx5IHJld3JpdGVzIGlmIHlvdSBjaGFuZ2UgdGhpbmdzDQogICAgICAgICAgICAgICBjYWNoZSA9IFRSVUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgIyBhdXRvbWF0aWNhbGx5IGRvd25sb2FkcyBkZXBlbmRlbmN5IGZpbGVzDQogICAgICAgICAgICAgICBhdXRvZGVwID0gVFJVRSwNCiAgICAgICAgICAgICAgICMgDQogICAgICAgICAgICAgICBjYWNoZS5jb21tZW50cyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgIyANCiAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSwNCiAgICAgICAgICAgICAgICMgY2hhbmdlIGZpZy53aWR0aCBhbmQgZmlnLmhlaWdodCB0byBjaGFuZ2UgdGhlIGNvZGUgaGVpZ2h0IGFuZCB3aWR0aCBieSBkZWZhdWx0DQogICAgICAgICAgICAgICBmaWcud2lkdGggPSA1LjUsICANCiAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA0LjUsDQogICAgICAgICAgICAgICBmaWcuYWxpZ249J2NlbnRlcicpDQoNCg0KYGBgDQoNCmBgYHtyIHNldHVwLTJ9DQoNCiMgQWx3YXlzIHByaW50IHRoaXMgb3V0IGJlZm9yZSB5b3VyIGFzc2lnbm1lbnQNCnNlc3Npb25JbmZvKCkNCmdldHdkKCkNCg0KYGBgDQoNCg0KPCEtLSAjIyMgc3RhcnQgYW5zd2VyaW5nIHlvdXIgcHJvYmxlbSBzZXQgaGVyZSAtLT4NCjwhLS0gWW91IG1heSBleHBvcnQgeW91ciBob21ld29yayBpbiBlaXRoZXIgaHRtbCBvciBwZGYsIHdpdGggdGhlIGZvcm1lciB1c3VhbGx5IGJlaW5nIGVhc2llci4gDQogICAgIFRvIGV4cG9ydCBvciBjb21waWxlIHlvdXIgUm1kIGZpbGU6IGNsaWNrIGFib3ZlIG9uICdLbml0JyB0aGVuICdLbml0IHRvIEhUTUwnIC0tPg0KPCEtLSBCZSBzdXJlIHRvIHN1Ym1pdCBib3RoIHlvdXIgLlJtZCBmaWxlIGFuZCB0aGUgY29tcGlsZWQgLmh0bWwgb3IgLnBkZiBmaWxlIGZvciBmdWxsIGNyZWRpdCAtLT4NCg0KDQpgYGB7ciBzZXR1cC0zfQ0KDQojIGxvYWQgYWxsIHlvdXIgbGlicmFyaWVzIGluIHRoaXMgY2h1bmsgDQpsaWJyYXJ5KCd0aWR5dmVyc2UnKQ0KbGlicmFyeSgiZnMiKQ0KbGlicmFyeSgnaGVyZScpDQpsaWJyYXJ5KCdkcGx5cicpDQpsaWJyYXJ5KCd0aWR5dmVyc2UnKQ0KbGlicmFyeSgnZ2dwbG90MicpDQpsaWJyYXJ5KCdnZ3JlcGVsJykNCmxpYnJhcnkoJ2dndGhlbWVzJykNCmxpYnJhcnkoJ2ZvcmNhdHMnKQ0KbGlicmFyeSgncnNhbXBsZScpDQpsaWJyYXJ5KCdsdWJyaWRhdGUnKQ0KbGlicmFyeSgnZ2d0aGVtZXMnKQ0KbGlicmFyeSgna2FibGVFeHRyYScpDQpsaWJyYXJ5KCdwYXN0ZWNzJykNCmxpYnJhcnkoJ3ZpcmlkaXMnKQ0KbGlicmFyeSgncGxvdGx5JykNCmxpYnJhcnkoJ3RpZHlxdWFudCcpDQpsaWJyYXJ5KCdzY2FsZXMnKQ0KDQoNCiMgbm90ZSwgZG8gbm90IHJ1biBpbnN0YWxsLnBhY2thZ2VzKCkgaW5zaWRlIGEgY29kZSBjaHVuay4gaW5zdGFsbCB0aGVtIGluIHRoZSBjb25zb2xlIG91dHNpZGUgb2YgYSBjb2RlIGNodW5rLiANCg0KYGBgDQoNCg0KDQojIyBGaW5hbCBQcm9qZWN0IENsZWFuaW5nIGFuZCBTdW1tYXJ5IFN0YXRpc3RpY3MgDQoNCjFhKSBMb2FkaW5nIGRhdGENCg0KYGBge3J9DQoNCiNSZWFkaW5nIHRoZSBkYXRhIGluIGFuZCBkb2luZyBtaW5vciBpbml0aWFsIGNsZWFuaW5nIGluIHRoZSBmdW5jdGlvbiBjYWxsDQojUmVwcm9kdWNpYmxlIGRhdGEgYW5hbHlzaXMgc2hvdWxkIGF2b2lkIGFsbCBhdXRvbWF0aWMgc3RyaW5nIHRvIGZhY3RvciBjb252ZXJzaW9ucy4NCiNzdHJpcC53aGl0ZSByZW1vdmVzIHdoaXRlIHNwYWNlIA0KI25hLnN0cmluZ3MgaXMgYSBzdWJzdGl0dXRpb24gc28gYWxsIHRoYXQgaGF2ZSAiIiB3aWxsID0gbmENCmRhdGEgPC0gcmVhZC5jc3YoaGVyZTo6aGVyZSgiZmluYWxfcHJvamVjdCIsICJkb25vcl9kYXRhLmNzdiIpLA0KICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsDQogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQ0KDQpgYGANCg0KDQoxYikgRml4aW5nIHRoZSB3b25reSBET0IgJiBEYXRhIGNsZWFudXANCg0KYGBge3J9DQoNCiMoQmlydGhkYXRlIGFuZCBBZ2UsIElEIGFzIGEgbnVtYmVyKWFkZGluZyBET0IgKEFnZS9TcG91c2UgQWdlKSBpbiB5ZWFycyBjb2x1bW5zIGFuZCBhZGRpbmcgdHdvIGZpZWxkcyBmb3IgYXNzaWdubWVudCBhbmQgbnVtYmVyIG9mIGNoaWxkcmVuDQpkYXRhY2xlYW5pbmcgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKEJpcnRoZGF0ZSA9IGlmZWxzZShCaXJ0aGRhdGUgPT0gIjAwMDEtMDEtMDEiLCBOQSwgQmlydGhkYXRlKSkgJT4lDQogIG11dGF0ZShCaXJ0aGRhdGUgPSBtZHkoQmlydGhkYXRlKSkgJT4lDQogIG11dGF0ZShBZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBCaXJ0aGRhdGUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lDQogIG11dGF0ZShTcG91c2UuQmlydGhkYXRlID0gaWZlbHNlKFNwb3VzZS5CaXJ0aGRhdGUgPT0gIjAwMDEtMDEtMDEiLCBOQSwgU3BvdXNlLkJpcnRoZGF0ZSkpICU+JQ0KICBtdXRhdGUoU3BvdXNlLkJpcnRoZGF0ZSA9IG1keShTcG91c2UuQmlydGhkYXRlKSkgJT4lDQogIG11dGF0ZShTcG91c2UuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gU3BvdXNlLkJpcnRoZGF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lDQogIG11dGF0ZShJRCA9IGFzLm51bWVyaWMoSUQpKSAlPiUgDQogIG11dGF0ZShBc3NpZ25tZW50X2ZsYWcgPSBpZmVsc2UoaXMubmEoQXNzaWdubWVudC5OdW1iZXIpLCAwLDEpKSAlPiUgDQogIG11dGF0ZSggTm9fb2ZfQ2hpbGRyZW4gPSBpZmVsc2UoaXMubmEoQ2hpbGQuMS5JRCksMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoQ2hpbGQuMi5JRCksMSwyKSkpDQoNCiNzcGxpdHRpbmcgdXAgdGhlIGFnZSBpbnRvIHJhbmdlcyBhbmQgY3JlYXRpbmcgY2F0ZWdvcnkgZm9yIGVhc3kgdmlzdWFsaXphdGlvbiANCmRhdGFjbGVhbmluZyA8LSBkYXRhY2xlYW5pbmcgJT4lDQogIG11dGF0ZShhZ2VfcmFuZ2UgPSANCiAgICBpZmVsc2UoQWdlICVpbiUgMTA6MTksICIxMCA8IDIwIHllYXIgb2xkcyIsDQogICAgaWZlbHNlKEFnZSAlaW4lIDIwOjI5LCAiMjAgPCAzMCB5ZWFyIG9sZHMiLCANCiAgICBpZmVsc2UoQWdlICVpbiUgMzA6MzksICIzMCA8IDQwIHllYXIgb2xkcyIsDQogICAgaWZlbHNlKEFnZSAlaW4lIDQwOjQ5LCAiNDAgPCA1MCB5ZWFyIG9sZHMiLA0KICAgIGlmZWxzZShBZ2UgJWluJSA1MDo1OSwgIjUwIDwgNjAgeWVhciBvbGRzIiwNCiAgICBpZmVsc2UoQWdlICVpbiUgNjA6NjksICI2MCA8IDcwIHllYXIgb2xkcyIsDQogICAgaWZlbHNlKEFnZSAlaW4lIDcwOjc5LCAiNzAgPCA4MCB5ZWFyIG9sZHMiLA0KICAgIGlmZWxzZShBZ2UgJWluJSA4MDo4OSwgIjgwIDwgOTAgeWVhciBvbGRzIiwNCiAgICBpZmVsc2UoQWdlICVpbiUgOTA6OTksICI5MCA8IDEwMCB5ZWFyIG9sZHMiLA0KICAgIGlmZWxzZShBZ2UgJWluJSAxMDA6MTA5LCAiMTAwIDwgMTEwIHllYXIgb2xkcyIsDQogICAgaWZlbHNlKEFnZSAlaW4lIDExMDoxMjAsICIxMTAgLSAxMjAgIHllYXIgb2xkcyIsDQogICAgTkEpKSkpKSkpKSkpKSkNCg0KI3NwbGl0dGluZyB6aXBjb2RlIHNhbGFyeSBpbnRvIHJhbmdlcyBmb3IgZWFzeSB2aXN1YWxpemF0aW9uIA0KZGF0YV9jbGVhbmVkIDwtIGRhdGFfY2xlYW5lZCAlPiUNCiAgbXV0YXRlKHppcHNscnlfcmFuZ2UgPSANCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgOTAwMDA6OTkwMDAsICI5MEstOTlLIiwNCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMTAwMDAwOjE0OTAwMCwgIjEwMEstMTQ5SyIsIA0KICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAxNTAwMDA6MTk5MDAwLCAiMTUwSy0xOTlLIiwNCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMjAwMDAwOjI0OTAwMCwgIjIwMEstMjQ5SyIsDQogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDI1MDAwMDoyOTkwMDAsICIyNTBLLTI5OUsiLA0KICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAzMDAwMDA6MzQ5MDAwLCAiMzAwSy0zNDlLIiwNCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMzUwMDAwOjM5OTAwMCwgIjM1MEstMzk5SyIsDQogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDQwMDAwMDo0OTkwMDAsICI0MDBLLTQ5OUsiLA0KICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSA1MDAwMDA6OTk5MDAwLCAiNTAwSy05OTlLIiwNCiAgICBOQSkpKSkpKSkpKSkNCg0KZ2xpbXBzZShkYXRhX2NsZWFuZWQkemlwc2xyeV9yYW5nZSkNCg0KDQojc2VlaW5nIHdoYXQgd2UgaGF2ZQ0KdGFibGUoZGF0YWNsZWFuaW5nJGFnZV9yYW5nZSkNCiM1MC02MCBpcyB0aGUgbW9zdCBjb21tb24gYWdlIHJhbmdlIA0KDQojUmVtb3ZpbmcgQ29sdW1ucyB0aGF0IHByb3ZpZGUgbm8gYmVuZWZpdCANCg0KZGF0YV9jbGVhbmVkX2NvbHVtbnMgPC0gc3Vic2V0KGRhdGFjbGVhbmluZyxzZWxlY3QgPSAtYyhBc3NpZ25tZW50Lk51bWJlcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5oYXMuSGlzdG9yaWNhbC5NbmdyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTdWZmaXgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuRGF0ZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5NYW5hZ2VyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlJvbGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuVGl0bGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuU3RhdHVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTdHJhdGVneQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUHJvZ3Jlc3MuTGV2ZWwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuR3JvdXANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuQ2F0ZWdvcnkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEZ1bmRpbmcuTWV0aG9kDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkRhdGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFF1YWxpZmljYXRpb24uQW1vdW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkFtb3VudA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsRXhwZWN0ZWQuQm9vay5EYXRlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxIYXJkLkdpZnQuVG90YWwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFNvZnQuQ3JlZGl0LlRvdGFsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxUb3RhbC5Bc3NpZ25tZW50LkdpZnRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxOby5vZi5QbGVkZ2VzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQcm9wb3NhbC4uDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQcm9wb3NhbC5Ob3Rlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSEguTGlmZS5IYXJkLkNyZWRpdA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSEguTGlmZS5Tb2Z0LkNyZWRpdA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSEguTGlmZS5TcG91c2UuQ3JlZGl0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxMYXN0LkNvbnRhY3QuQnkuTWFuYWdlcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsWC4ub2YuQ29udGFjdHMuQnkuTWFuYWdlcikpDQojY2xlYW5pbmcgdXAgemlwIGNvZGVzIHJlbW92aW5nIC00IGFmdGVyIA0KZGF0YV9jbGVhbmVkX2NvbHVtbnMkWmlwIDwtIGdzdWIoZGF0YV9jbGVhbmVkX2NvbHVtbnMkWmlwLCBwYXR0ZXJuPSItLioiLCByZXBsYWNlbWVudCA9ICIiKQ0KDQojYWRkaW5nIHppcCBjb2RlIGRhdGEgYW5kIGNvbHVtbiANCnppcCA8LSByZWFkLmNzdihoZXJlOjpoZXJlKCJmaW5hbF9wcm9qZWN0IiwgIlNhbGFyeV9aaXBjb2RlLmNzdiIpLA0KICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsDQogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQ0KDQojYWRkaW5nIHppcCBzYWxhcnkgY29sdW1uDQpkYXRhX2NsZWFuZWRfY29sdW1ucyA8LWRhdGFfY2xlYW5lZF9jb2x1bW5zICU+JQ0KICAgIG11dGF0ZSh6aXBjb2RlX3NscnkgPSBWTE9PS1VQKFppcCwgemlwLCBOQU1FLCBTMTkwMl9DMDNfMDAyRSkpDQoNCiNhZGRpbmcgc2Nob2xhcnNoaXAgZGF0YSAoeS9uKQ0Kc2NobHIgPC0gcmVhZC5jc3YoaGVyZTo6aGVyZSgiZmluYWxfcHJvamVjdCIsICJzY2hvbGFyc2hpcC5jc3YiKSwNCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICBzdHJpcC53aGl0ZSA9IFRSVUUsDQogICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSAiIikNCg0KI2FkZGluZyBzY2hvbGFyc2hpcCBjb2x1bW4NCmRhdGFfY2xlYW5lZF9jb2x1bW5zIDwtZGF0YV9jbGVhbmVkX2NvbHVtbnMgJT4lDQogICAgbXV0YXRlKHNjaG9sYXJzaGlwID0gVkxPT0tVUChJRCwgc2NobHIsIElELCBTQ0hPTEFSU0hJUCkpIA0KDQojcmVwbGFjaW5nIE5BIHdpdGggMCANCiBkYXRhX2NsZWFuZWRfY29sdW1ucyRzY2hvbGFyc2hpcCA8LSByZXBsYWNlX25hKGRhdGFfY2xlYW5lZF9jb2x1bW5zJHNjaG9sYXJzaGlwLCcwJykNCiANCiNyZXBsYWNpbmcgWSB3aXRoIDEgDQpkYXRhX2NsZWFuZWRfY29sdW1ucyRzY2hvbGFyc2hpcDwtaWZlbHNlKGRhdGFfY2xlYW5lZF9jb2x1bW5zJHNjaG9sYXJzaGlwPT0iWSIsMSwwKQ0KDQojY2hlY2tpbmcgaG93IG1hbnkgYXJlIE4NCnRhYmxlKGRhdGFfY2xlYW5lZF9jb2x1bW5zJHNjaG9sYXJzaGlwKQ0KDQoNCiNjaGVja2luZyBhbmQgZGVsZXRpbmcgc2Nob2xhcnNoaXAgY29sdW1uIA0KY2xhc3MoZGF0YV9jbGVhbmVkX2NvbHVtbnMkc2NobHJfZmN0KQ0KZGF0YV9jbGVhbmVkX2NvbHVtbnMgPSBzdWJzZXQoZGF0YV9jbGVhbmVkX2NvbHVtbnMsIHNlbGVjdCA9IC1jKHNjaG9sYXJzaGlwKSkNCiAgDQojY2hlY2tpbmcgZm9yIGR1cGxpY2F0ZXMgTiA+MSBpbmRpY2F0ZXMgYSByZWNvcmRzIHZhbHVlcyBhcmUgaW4gdGhlIGZpbGUgdHdpY2UgDQpkYXRhX2NsZWFuZWRfY29sdW1ucyAlPiUgZ3JvdXBfYnkoSUQpICU+JSBjb3VudCgpICU+JSBhcnJhbmdlKGRlc2MobikpDQoNCiNyZW1vdmluZyBkdXBsaWNhdGVkIHJlY29yZHMNCg0KZGF0YV9jbGVhbmVkIDwtIHVuaXF1ZShkYXRhX2NsZWFuZWRfY29sdW1ucykNCg0KI24gPSAxIG5vIElEIHdpdGggbXVsdGlwbGUgcmVjb3JkcyBjbGVhbmVkIG9mIGR1cGVzDQpkYXRhX2NsZWFuZWQgJT4lIGdyb3VwX2J5KElEKSAlPiUgY291bnQoKSAlPiUgYXJyYW5nZShkZXNjKG4pKQ0KDQpgYGANCg0KMWMgQ3JlYXRpbmcgZmFjdG9yIHZhcmlhYmxlIGZvciBzZXggYW5kIG1hcnJpZWQgDQoNCmBgYHtyfQ0KDQpkYXRhX2NsZWFuZWQgPC0gDQogIGRhdGFfY2xlYW5lZCAlPiUgDQogIG11dGF0ZShzZXhfZmN0ID0gDQogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTZXgpDQogICkNCg0KDQpkYXRhX2NsZWFuZWQgPC0NCmRhdGFfY2xlYW5lZCAlPiUgDQptdXRhdGUoDQogIHNleF9zaW1wbGUgPSANCiAgICBmY3RfbHVtcF9uKFNleCwgbiA9IDQpDQopDQoNCiNjaGVja2luZyB0byBzZWUgaWYgaXRzIGEgZmFjdG9yDQpjbGFzcyhkYXRhX2NsZWFuZWQkc2V4X2ZjdCkNCg0KI2NoZWNraW5nIGxldmVscw0KbGV2ZWxzKGRhdGFfY2xlYW5lZCRzZXhfc2ltcGxlKQ0KDQojY3JlYXRpbmcgYSB0YWJsZSBhZ2FpbnN0IFNleCBjb2x1bW4gDQp0YWJsZShkYXRhX2NsZWFuZWQkc2V4X2ZjdCwgZGF0YV9jbGVhbmVkJHNleF9zaW1wbGUpDQoNCiNtYWtpbmcgbWFycmllZCBhIGZhY3RvciANCmRhdGFfY2xlYW5lZF9jb2x1bW5zIDwtIA0KICBkYXRhX2NsZWFuZWRfY29sdW1ucyAlPiUgDQogIG11dGF0ZShtYXJyaWVkX2ZjdCA9IA0KICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWFycmllZCkNCiAgKQ0KDQojY2hlY2tpbmcgdG8gc2VlIGlmIGl0cyBhIGZhY3Rvcg0KY2xhc3MoZGF0YV9jbGVhbmVkJG1hcnJpZWRfZmN0KQ0KDQoNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCg0KDQoNCmBgYA0KDQoxZCAjTWVhbiwgTWVkaWFuLCBhbmQgQ291bnQgb2YgR2l2aW5nIGluIEFnZSBSYW5nZXMgDQoNCmBgYHtyfQ0KDQphZ2VfcmFuZ2VfZ2l2aW5nIDwtIGRhdGFjbGVhbmluZyAlPiUNCiAgZ3JvdXBfYnkoYWdlX3JhbmdlKSAlPiUNCiAgc3VtbWFyaXNlKGF2Z19naXZpbmcgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIG1lZF9naXZpbmcgPSBtZWRpYW4oSEguTGlmZXRpbWUuR2l2aW5nLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgYW1vdW50X29mX3Blb3BsZV9pbl9hZ2VfcmFuZ2UgPSBuKCkpDQoNCg0KDQpgYGANCg0KDQoNCg0KDQojIyBRdWVzdGlvbiAyDQoNCkRvbm9yU2VnbWVudCBBbmFseXNpcw0KDQpgYGB7cn0NCiNncm91cGluZyBieSBkb25vcnNlZ21lbnQgYW5kIGFuYWx5emluZyANCmRhdGFfY2xlYW5lZF9jb2x1bW5zICU+JQ0KICBncm91cF9ieShEb25vci5TZWdtZW50KSAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKERvbm9yLlNlZ21lbnQpLA0KICAgICAgICAgICAgbWVhbl90b3RhbF9naXYgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZykpICU+JQ0KICBhcnJhbmdlKC1Db3VudCkgJT4lDQogIGZpbHRlcihDb3VudCA+PSAxMDApICU+JQ0KICAjYWRkZWQgc2NhbGVzIHBhY2thZ2UgdG8gaGF2ZSB0aGUgdmFsdWVzIHNob3cgaW4gZG9sbGFyIA0KICBtdXRhdGUobWVhbl90b3RhbF9naXYgPSBkb2xsYXIobWVhbl90b3RhbF9naXYpKSAlPiUNCiAga2FibGUoY29sLm5hbWVzID0gYygiRG9ub3IgU2VnbWVudCIsICJDb3VudCIsICJNZWFuIEhIIExpZmV0aW1lIEdpdmluZyIpLCBhbGlnbj1yZXAoJ2MnLCAzKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksDQogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpDQogIA0KDQpgYGANCg0KMmEpIFBsb3R0aW5nIGF2ZXJhZ2UgZ2l2aW5nIGJ5IGFnZSByYW5nZSANCg0KDQpgYGB7cn0NCg0KZ2dwbG90KGFnZV9yYW5nZV9naXZpbmcsIGFlcyhhdmdfZ2l2aW5nLCBhZ2VfcmFuZ2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQ0KDQoNCmBgYA0KDQoNCjJiKSBDb3VudCBvZiBkb25vcnMgYmFzZWQgb24gYWdlIHJhbmdlIChhbm90aGVyIHdheSB0byBsb29rIGF0IGl0KQ0KDQoNCmBgYHtyfQ0KDQpnZ3Bsb3QoZGF0YWNsZWFuaW5nLCANCiAgICAgICBhZXMoYWdlX3JhbmdlKSkgKyANCiAgICAgICBnZW9tX2JhcigpICsgDQogICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3Q9MSkpICsgDQogIGxhYnModGl0bGUgPSAiQ291bnQgb2YgQWdlIFJhbmdlcyIsIHggPSAiIiwgeSA9ICIiKQ0KICANCg0KYGBgDQoNCjJjKSBCb3hwbG90IG9mIHRoZSBBZ2UgUmFuZ2VzIEFnYWluc3QgdGhlIExpZmV0aW1lIEdpdmluZyBBbW91bnRzIHdpdGggYSBsb2cgc2NhbGUgYXBwbGllZCAtIHRoZSByZWFzb24gd2UgYXBwbGllZCBsb2cgc2NhbGUgaXMgdG8gcmVzb2x2ZSBpc3N1ZXMgd2l0aCB2aXN1YWxpemF0aW9ucyB0aGF0IHNrZXcgdG93YXJkcyBsYXJnZSB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIA0KDQoNCmBgYHtyfQ0KDQpnZ3Bsb3QoZGF0YWNsZWFuaW5nLCBhZXMoYWdlX3JhbmdlLEhILkxpZmV0aW1lLkdpdmluZyxmaWxsID0gYWdlX3JhbmdlKSkgKyANCiAgZ2VvbV9ib3hwbG90KA0KICBvdXRsaWVyLmNvbG91ciA9ICJyZWQiKSArIA0KICBzY2FsZV95X2xvZzEwKCkgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpDQogIA0KDQpgYGANCg0KMmQpIFNwbGl0dGluZyBieSBhZ2UgYW5kIGdlbmRlciANCg0KDQpgYGB7cn0NCg0KDQojY3JlYXRpbmcgYm94cGxvdHMgDQpkYXRhY2xlYW5pbmcgJT4lIA0KICBmaWx0ZXIoQWdlIDwgMTAwKSAlPiUgI3JlbW92aW5nIHRoZSB3ZWlyZCBvdXRsaWVycyB0aGF0IGFyZSBvdmVyIDEwMCANCiAgZmlsdGVyKFNleCAlaW4lIGMoIk0iLCAiRiIpKSAlPiUNCiAgZ2dwbG90KGFlcyhTZXgsIEFnZSkpICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIHRoZW1lX2Vjb25vbWlzdCgpICsgDQogIGdndGl0bGUoIkFnZXMgb2YgRG9ub3JzIEJhc2VkIG9uIEdlbmRlciIpICsgDQogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpDQogIA0KICANCiAgDQoNCg0KYGBgDQoNCjJlKSBEaXN0cmlidXRpb24gb2YgcGVvcGxlIGluIHRoZSBzdGF0ZXMgdGhhdCB0aGV5IGxpdmUuDQoNCmBgYHtyfQ0KDQogIGRhdGFjbGVhbmluZyAlPiUNCiAgbXV0YXRlKFN0YXRlID0gaWZlbHNlKFN0YXRlID09ICIgIiwgIk5BIiwgU3RhdGUpKSAlPiUNCiAgZmlsdGVyKFN0YXRlICE9ICJOQSIpICU+JQ0KICBncm91cF9ieShTdGF0ZSkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChTdGF0ZSkpICU+JQ0KICBmaWx0ZXIoQ291bnQgPiA4MDApICU+JQ0KICBhcnJhbmdlKC1Db3VudCkgJT4lDQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIkRvbm9yJ3MgU3RhdGUiLCAiQ291bnQiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJjb25kZW5zZWQiKSwNCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikNCiAgDQogDQogIA0KICANCg0KDQpgYGANCg0KMmYpIExvb2tpbmcgYXQgYWxsIGRvbm9ycyBmaXJzdCBnaWZ0IGFtb3VudC4gNzUlIG1hZGUgYSBmaXJzdCBnaWZ0IG9mIDwxMDAuIA0KDQpgYGB7cn0NCg0KIG5vX25vbl9kb25vcnMgPC0gZGF0YWNsZWFuaW5nICU+JQ0KICBmaWx0ZXIoTGlmZXRpbWUuR2l2aW5nICE9IDApDQogIA0KbmQgPC0gcXVhbnRpbGUobm9fbm9uX2Rvbm9ycyRISC5GaXJzdC5HaWZ0LkFtb3VudCwgcHJvYnMgPSBjKC4yNSwuNTAsLjc1LC45LC45OSksIG5hLnJtID0gVFJVRSkNCg0KbmQgPC0gYXMuZGF0YS5mcmFtZShuZCkNCg0KbmQgJT4lDQogIGthYmxlKGNvbC5uYW1lcyA9ICJRdWFudGlsZSIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLA0KICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGKQ0KICANCiAgDQoNCg0KYGBgDQoNCg0KDQojIyBNb2RlbGluZyBmb3IgeW91IA0KDQoNCjNhKSBMaW5lYXIgbW9kZWwgDQoNCmBgYHtyfQ0KI2NvbnZlcnRpbmcgbWFycmllZCBZIGFuZCBOIHRvIDEgYW5kIDAgDQpkYXRhY2xlYW5pbmcgPC0gZGF0YWNsZWFuaW5nICU+JQ0KICAgICAgbXV0YXRlKE1hcnJpZWRfc2ltcGxlID0gaWZlbHNlKE1hcnJpZWQgPT0gIk4iLDAsMSkpDQogDQoNCm1vZDFsbSA8LSBsbSggTWFycmllZF9zaW1wbGUgfiBMaWZldGltZS5HaXZpbmcsDQogICAgICAgICAgIGRhdGEgPSBkYXRhY2xlYW5pbmcpDQoNCnN1bW1hcnkobW9kMWxtKQ0KICANCg0KDQpgYGANCkttZWFucw0KYGBge3J9DQoNCnByZWRfdmFycyA8LSBjKCdtYXJyaWVkX2ZjdCcsICdzZXhfZmN0JykgDQogDQogDQpkYXRhX2NsZWFuZWRfSyA8LSBzZWxlY3QoZGF0YV9jbGVhbmVkLA0KICAgICAgICAgICAgICAgICAgICAgcHJlZF92YXJzLA0KICAgICAgICAgICAgICAgICAgICAgSEguTGlmZXRpbWUuR2l2aW5nKQ0KIA0KI2J1aWxkIGNsdXN0ZXINCmRkX2ttZWFucyA8LSBrbWVhbnMoeCA9IGRhdGFfY2xlYW5lZF9LLCANCiAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IDUsIA0KICAgICAgICAgICAgICAgICAgICBuc3RhcnQgPSAxMCkNCg0KaXMubnVtZXJpYyhkYXRhX2NsZWFuZWQkSEguTGlmZXRpbWUuR2l2aW5nKQ0KDQpgYGANCg0KDQoNCg0KDQozYSkgDQoNCmBgYHtyfQ0KcCA8LSBkYXRhY2xlYW5pbmcgJT4lDQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLCBmaWxsID0gImJsdWUiKSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsNCiAgZ2d0aXRsZSgiT3ZlcmFsbCBEb25vciBBZ2UgRGlzdHJpYnV0aW9uIikgKyANCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDUsMTAwLGJ5ID0gMjApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsMTAwLGJ5ID0gMjApKSArIHhsaW0oYygyMCwxMDApKQ0KDQpnZ3Bsb3RseShwKQ0KICANCnANCg0KZ2dwbG90KGRhdGEgPSBkYXRhY2xlYW5pbmcsIGFlcyh4ID0gQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0iYmx1ZSIpKyB4bGltKGMoMjAsMTAwKSkNCg0KICANCg0KDQpgYGANCg==